框架选择:

根据 https://blog.zengrong.net/pos...
得知:
flask项目本身使用的是pytest
nose是对标准库unittest的封装,现在比较流行,但文档没有pytest做的好,且近几年一直处于维护状态没有更新。
Flask-Testing flask扩展
最终选择:pytest

pytest

特点:

  • 非常容易上手,入门简单,文档丰富,文档中有很多实例可以参考
  • 能够支持简单的单元测试和复杂的功能测试
  • 支持参数化
  • 执行测试过程中可以将某些测试跳过,或者对某些预期失败的case标记成失败
  • 支持重复执行失败的case
  • 支持运行由nose, unittest编写的测试case
  • 具有很多第三方插件,并且可以自定义扩展
  • 方便的和持续集成工具集成

在命令行输入如下命令检查pytest是否已安装

py.test --version

如果没有

pip install -U pytest

第一个例子:测试函数

# content of test_sample.py


def func(x):
    return x+1


def test_func():
    assert func(3) == 5

运行:
执行测试时需要下面几步:

  1. 从命令行进入测试文件所在目录,pytest会在该目录中寻找以test开头的文件
  2. 找到测试文件,进入测试文件中寻找以test_开头的函数并执行
  3. 测试函数以断言assert结尾
$ py.test
============================= test session starts ==============================
platform darwin -- Python 3.5.1, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
rootdir: /Users/fc/project/test/pytest_sample, inifile:
collected 1 items

test_sample.py F

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

test_sample.py:9: AssertionError
=========================== 1 failed in 0.01 seconds ===========================

第二个例子: 测试类

# content of test_class.py


class TestClass(object):
    def test_one(self):
        x = 'this'
        assert 'h' in x

    def test_two(self):
        x = 'hello'
        assert hasattr(x, 'check')

运行
下面的-q是 quiet的意思,就是忽略一些很细节的信息
使用测试类时,注意下面几点:

  1. 测试类所在的文件以test_开头
  2. 测试类以Test开头,并且不能带有__init__方法
  3. 类中测试函数以test_开头
  4. 测试函数以assert断言结尾
bogon:pytest_sample fc$ py.test -q test_class.py
.F
=================================== FAILURES ===================================
______________________________ TestClass.test_two ______________________________

self = <test_class.TestClass object at 0x10595b080>

    def test_two(self):
        x = 'hello'
>       assert hasattr(x, 'check')
E       assert hasattr('hello', 'check')

test_class.py:11: AssertionError
1 failed, 1 passed in 0.01 seconds
第三个例子:参数化测试函数
import pytest

params = [
    (2, 3, 5),
    (4, 5, 9),
    (6, 7, 12)
]


@pytest.mark.parametrize('a, b, expected', params)
def test_add(a, b, expected):
    assert a + b == expected

运行结果

$ py.test -q test_params.py
..F
=================================== FAILURES ===================================
_______________________________ test_add[6-7-12] _______________________________

a = 6, b = 7, expected = 12

    @pytest.mark.parametrize('a, b, expected', params)
    def test_add(a, b, expected):
>       assert a + b == expected
E       assert (6 + 7) == 12

test_params.py:12: AssertionError
1 failed, 2 passed in 0.01 seconds

说明:

  1. params是要进行测试的参数list,其中元素为tuple,每个tuple对应一套参数
  2. @pytest.mark.parametrize装饰器的第一个参数是一个字符串,不过这个字符串其实是以逗号分隔的一组参数,这个参数就是其所装饰的函数的参数。
  3. @pytest.mark.parametrize装饰器将params中的参数一套一套拿出来放入其所装饰的函数中执行

第四个例子 fixture params

import pytest


@pytest.fixture(params=[1, 2, 3])
def test_data(request):
    return request.param


def test_not_2(test_data):
    assert test_data != 2

运行结果:

$ py.test -q fixture_params.py
.F.
======================================= FAILURES =======================================
____________________________________ test_not_2[2] _____________________________________

test_data = 2

    def test_not_2(test_data):
>       assert test_data != 2
E       assert 2 != 2

fixture_params.py:10: AssertionError
1 failed, 2 passed in 0.01 seconds

说明:

  1. 把一个函数定用@pytest.fixture装饰,那这个函数就是fixture函数
  2. 一个fixture函数可以被其他测试函数调用,将函数名当作参数即可,fixture的返回值会当作测试函数的参数
  3. fixture函数中的params字段默认为None,如果有值,则每个值都会调用执行一次

在flask项目中使用pytest

flask应用demo代码

from flask import Flask


app = Flask(__name__)


@app.route('/')
def index():
    return 'hello'


@app.route('/login')
def login():
    return 'login'


@app.route('/logout')
def logout():
    return 'logout'


@app.errorhandler(404)
def page_not_found():
    return '404'


if __name__ == '__main__':
    app.run()

flask demo的测试代码

from flaskr import app


class TestClass(object):
    def setup_class(self):
        """测试开始时候执行, 用来做准备工作,一般用来初始化资源。"""
        app.config['TESTING'] = True  # 这将会使得处理请求时的错误捕捉失效,以便于 您在进行对应用发出请求的测试时获得更好的错误反馈。
        # 测试客户端将会给我们一个通向应用的简单接口,我们可以激发 对向应用发送请求的测试,并且此客户端也会帮我们记录 Cookie 的 动态。
        self.app = app.test_client()

    def teardown_class(self):
        """测试结束时执行, 用来做收尾工作, 一般用来关闭资源"""
        pass

    def test_login(self):
        response = self.app.get('/login')
        assert b'login' == response.data

    def test_logout(self):
        response = self.app.get('logout')
        assert b'logout' == response.data

    def test_index(self):
        response = self.app.get('/')
        assert b'hello' == response.data

梦里秋天
0 声望0 粉丝

一个简单的理想主义者